#ifndef ARM_PSCI_H
#define ARM_PSCI_H
#include "tools/macros.h"

#define PSCI_0_2_FN_BASE   0x84000000
#define PSCI_0_2_FN(n)     (PSCI_0_2_FN_BASE + (n))
#define PSCI_0_2_64BIT     0x40000000
#define PSCI_0_2_FN64_BASE (PSCI_0_2_FN_BASE + PSCI_0_2_64BIT)
#define PSCI_0_2_FN64(n)   (PSCI_0_2_FN64_BASE + (n))

#define PSCI_0_2_FN_PSCI_VERSION        PSCI_0_2_FN(0)   // Return the version of PSCI implemented.
#define PSCI_0_2_FN_CPU_SUSPEND         PSCI_0_2_FN(1)   // Suspend execution on a core or higher level topology node. This call is intended for use in idle subsystems where the core is expected to return to execution through a wakeup event.
#define PSCI_0_2_FN_CPU_OFF             PSCI_0_2_FN(2)   // Power down the calling core. This call is intended for use in hotplug. A core that is powered down by CPU_OFF can only be powered up again in response to a CPU_ON
#define PSCI_0_2_FN_CPU_ON              PSCI_0_2_FN(3)   // Power up a core. This call is used to power up cores that either
#define PSCI_0_2_FN_AFFINITY_INFO       PSCI_0_2_FN(4)   // Enable the caller to request status of an affinity instance
#define PSCI_0_2_FN_MIGRATE             PSCI_0_2_FN(5)   // Optional. This is used to ask a uniprocessor Trusted OS to migrate its context to a specific core
#define PSCI_0_2_FN_MIGRATE_INFO_TYPE   PSCI_0_2_FN(6)   // Optional. This function allows a caller to identify the level of multicore support present in the Trusted OS
#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7)   // Optional. For a uniprocessor Trusted OS, this function returns the current resident core.
#define PSCI_0_2_FN_SYSTEM_OFF          PSCI_0_2_FN(8)   // Shutdown the system
#define PSCI_0_2_FN_SYSTEM_RESET        PSCI_0_2_FN(9)   // Reset the system

#define PSCI_0_2_FN64_CPU_SUSPEND         PSCI_0_2_FN64(1)
#define PSCI_0_2_FN64_CPU_ON              PSCI_0_2_FN64(3)
#define PSCI_0_2_FN64_AFFINITY_INFO       PSCI_0_2_FN64(4)
#define PSCI_0_2_FN64_MIGRATE             PSCI_0_2_FN64(5)
#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7)

#define PSCI_1_0_FN_PSCI_FEATURES  PSCI_0_2_FN(10)
#define PSCI_1_0_FN_SYSTEM_SUSPEND PSCI_0_2_FN(14)

#define PSCI_1_0_FN64_SYSTEM_SUSPEND PSCI_0_2_FN64(14)

#define PSCI_0_2_POWER_STATE_ID_MASK    0xffff
#define PSCI_0_2_POWER_STATE_ID_SHIFT   0
#define PSCI_0_2_POWER_STATE_TYPE_SHIFT 16
#define PSCI_0_2_POWER_STATE_TYPE_MASK  (0x1 << PSCI_0_2_POWER_STATE_TYPE_SHIFT)
#define PSCI_0_2_POWER_STATE_AFFL_SHIFT 24
#define PSCI_0_2_POWER_STATE_AFFL_MASK  (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT)

#define PSCI_1_0_EXT_POWER_STATE_ID_MASK    0xfffffff
#define PSCI_1_0_EXT_POWER_STATE_ID_SHIFT   0
#define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT 30
#define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK  (0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT)

#define PSCI_0_2_AFFINITY_LEVEL_ON         0
#define PSCI_0_2_AFFINITY_LEVEL_OFF        1
#define PSCI_0_2_AFFINITY_LEVEL_ON_PENDING 2

#define PSCI_0_2_TOS_UP_MIGRATE    0
#define PSCI_0_2_TOS_UP_NO_MIGRATE 1
#define PSCI_0_2_TOS_MP            2

#define PSCI_VERSION_MAJOR_SHIFT 16
#define PSCI_VERSION_MINOR_MASK  ((1U << PSCI_VERSION_MAJOR_SHIFT) - 1)
#define PSCI_VERSION_MAJOR_MASK  ~PSCI_VERSION_MINOR_MASK
#define PSCI_VERSION_MAJOR(ver)  (((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT)
#define PSCI_VERSION_MINOR(ver)  ((ver) & PSCI_VERSION_MINOR_MASK)
#define PSCI_VERSION(maj, min) \
    ((((maj) << PSCI_VERSION_MAJOR_SHIFT) & PSCI_VERSION_MAJOR_MASK) | ((min) & PSCI_VERSION_MINOR_MASK))

#define PSCI_RET_SUCCESS          0
#define PSCI_RET_NOT_SUPPORTED    -1
#define PSCI_RET_INVALID_PARAMS   -2
#define PSCI_RET_DENIED           -3
#define PSCI_RET_ALREADY_ON       -4
#define PSCI_RET_ON_PENDING       -5
#define PSCI_RET_INTERNAL_FAILURE -6
#define PSCI_RET_NOT_PRESENT      -7
#define PSCI_RET_DISABLED         -8
#define PSCI_RET_INVALID_ADDRESS  -9

struct arm_smccc_res {
    uint64_t a0;
    uint64_t a1;
    uint64_t a2;
    uint64_t a3;
};

int32_t psci_init(void);
int32_t psci_cpu_on(uint64_t cpuid, uint64_t entry_point);
void do_smc(uint64_t function_id, uint64_t arg0, uint64_t arg1, uint64_t arg2, struct arm_smccc_res *res);

#endif
